using UnityEngine;
using UnityEngine.Assertions;

/// <summary>
/// Camera, Plane的扩展方式实现
/// </summary>
public static class Extension
{
    /// <summary>
    /// 点和法线确定的一个平面
    /// </summary>
    /// <param name="normal"></param>
    /// <param name="point"></param>
    /// <returns></returns>
    public static Vector4 GetPlane(Vector3 normal, Vector3 point)
    {
        return new Vector4(normal.x, normal.y, normal.z, -Vector3.Dot(normal, point));
    }

    /// <summary>
    /// 三点a, b, c确定的平面
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <param name="c"></param>
    /// <returns></returns>
    public static Vector4 GetPlane(Vector3 a, Vector3 b, Vector3 c)
    {
        Vector3 normal = Vector3.Normalize(Vector3.Cross(b - a, c - a));
        return GetPlane(normal, a);
    }

    /*//这个算法不对.
    /// <summary>
    /// UnityEngine.Plane转Vector4的Plane
    /// </summary>
    /// <param name="plane"></param>
    /// <returns></returns>
    public static Vector4 ToVector4(this Plane plane)
    {
        return new Vector4(plane.normal.x, plane.normal.y, plane.normal.z, -plane.distance);
    }
    */

    /// <summary>
    /// 获取Camera的远裁切面的4个点坐标.
    /// </summary>
    /// <param name="camera"></param>
    /// <returns></returns>
    public static Vector3[] GetFarClipPlanePoint(this Camera camera)
    {
        Vector3[] points = new Vector3[4];
        Transform trans = camera.transform;
        float distance = camera.farClipPlane;
        float halfFovRad = camera.fieldOfView * Mathf.Deg2Rad * 0.5f;
        float upLen = distance * Mathf.Tan(halfFovRad);
        float rightLen = upLen * camera.aspect;
        Vector3 farCenterPoint = trans.position + trans.forward * distance;
        Vector3 up = upLen * trans.up;
        Vector3 right = rightLen * trans.right;

        points[0] = farCenterPoint - right - up;//left-bottom
        points[1] = farCenterPoint + right - up;//right-bottom
        points[2] = farCenterPoint - right + up;//left-up
        points[3] = farCenterPoint + right + up;//right-up

        return points;
    }

    /// <summary>
    /// 获取Camera视锥体6个面
    /// </summary>
    /// <param name="camera"></param>
    /// <returns></returns>
    public static Vector4[] GetFrustumPlane(this Camera camera)
    {
        Vector4[] planes = new Vector4[6];
        Transform trans = camera.transform;
        Vector3 cameraPos = trans.position;
        Vector3[] points = camera.GetFarClipPlanePoint();

        planes[0] = GetPlane(cameraPos, points[0], points[2]);//left
        planes[1] = GetPlane(cameraPos, points[3], points[1]);//right
        planes[2] = GetPlane(cameraPos, points[1], points[0]);//bottom
        planes[3] = GetPlane(cameraPos, points[2], points[3]);//up
        planes[4] = GetPlane(-trans.forward, cameraPos + trans.forward * camera.nearClipPlane);//near
        planes[5] = GetPlane(trans.forward, cameraPos + trans.forward * camera.farClipPlane);//far
        //GeometryUtility.CalculateFrustumPlanes(camera);
        return planes;
    }

    /// <summary>
    /// 无GC获取Camera视锥体的6个面
    /// </summary>
    /// <param name="camera"></param>
    /// <param name="planes"></param>
    public static void GetFrustumPlaneNoneAllocate(this Camera camera, Vector4[] planes)
    {
        Assert.IsNotNull(planes);
        Assert.AreEqual(planes.Length, 6);

        Transform trans = camera.transform;
        Vector3 cameraPos = trans.position;
        Vector3[] points = camera.GetFarClipPlanePoint();

        planes[0] = GetPlane(cameraPos, points[0], points[2]);//left
        planes[1] = GetPlane(cameraPos, points[3], points[1]);//right
        planes[2] = GetPlane(cameraPos, points[1], points[0]);//bottom
        planes[3] = GetPlane(cameraPos, points[2], points[3]);//up
        planes[4] = GetPlane(-trans.forward, cameraPos + trans.forward * camera.nearClipPlane);//near
        planes[5] = GetPlane(trans.forward, cameraPos + trans.forward * camera.farClipPlane);//far
    }
}